﻿using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json.Linq;

namespace Solution
{
    /// <summary>
    /// ASP.NET MVC 身份验证过滤
    /// </summary>
    public class FormAuthorizeAttribute : AuthorizeAttribute
    {
        #region 构造

        public FormAuthorizeAttribute()
        {
        }
        private new string[] Roles { get; set; }
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException("HttpContext");
            }
            if (!httpContext.User.Identity.IsAuthenticated)
            {
                return false;
            }
            if (Roles == null)
            {
                return true;
            }
            if (Roles.Length == 0)
            {
                return true;
            }
            if (Roles.Intersect(FormTicketHelper.GetUserData().Split(',')).Any())
            {
                return true;
            }
            //if (Roles.Any(HttpContext.Current.User.IsInRole))
            //{
            //    return true;
            //}
            return false;
        }

        #endregion 构造

        #region 公共方法

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            var actionAttrs = filterContext.ActionDescriptor.GetCustomAttributes(true);
            if (actionAttrs.Any(x => x is AllowAnonymousAttribute))
            {
                return;
            }
            var actionAttr = actionAttrs.FirstOrDefault(x => x is FormAuthorizeAttribute);
            if (actionAttr != null)
            {
                ((FormAuthorizeAttribute)actionAttr).Authenticate(filterContext);
                return;
            }
            var controllerAttrs = filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(true);
            if (controllerAttrs.Any(x => x is AllowAnonymousAttribute))
            {
                return;
            }
            var controllerAttr = controllerAttrs.FirstOrDefault(x => x is FormAuthorizeAttribute);
            if (controllerAttr != null)
            {
                ((FormAuthorizeAttribute)controllerAttr).Authenticate(filterContext);
                return;
            }
            this.Authenticate(filterContext);
        }

        /// <summary>
        /// 自定义 授权认证
        /// </summary>
        /// <param name="filterContext"></param>
        public void Authenticate(AuthorizationContext filterContext)
        {
            var context = filterContext.RequestContext.HttpContext;
            //var isAuthenticated = context.Request.IsAuthenticated;
            //if (!string.IsNullOrEmpty(this.Roles))
            //{
            //    isAuthenticated = isAuthenticated && this.Roles.Split(',').Any(r => context.User.IsInRole(r));
            //}
            //if (isAuthenticated)
            //{
            //    return;
            //}

            string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower();
            string actionName = filterContext.ActionDescriptor.ActionName.ToLower();
            //获得此 controller/action 的角色权限，如：admin,mgr 如果没有限制返回 ""
            string roles = GetRoles(controllerName, actionName);//CacheHelper.GetCacheString("AUTHEN_ROLES");//GetRoles.GetActionRoles(actionName, controllerName);
            if (!string.IsNullOrWhiteSpace(roles) && !"*".Equals(roles))
            {
                this.Roles = roles.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            }
            if (context.User.Identity.IsAuthenticated)
            {
                base.OnAuthorization(filterContext);
                return;
            }
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.Result = new StandardJsonResult()
                {
                    Message = context.Request.IsAuthenticated ? "Please login" : "You don't have sufficient permission"
                };
            }
            else
            {
                filterContext.Result = new RedirectResult("/Authen/login?returnUrl=" + HttpContext.Current.Request.RawUrl);
            }
        }
        /// <summary>
        /// controller/action 的角色权限，如：admin,mgr 如果没有限制返回 ""
        /// </summary>
        /// <param name="controllerName"></param>
        /// <param name="actionName"></param>
        /// <returns></returns>
        private string GetRoles(string controllerName, string actionName)
        {
            string rolesStr = "";
            string roles = CacheHelper.GetCacheString($"AUTHEN_ROLES_{controllerName}_{actionName}");  //获得当前控制器的 角色字符串
            if (!string.IsNullOrEmpty(roles))
            {
                return roles;
            }

            string roleStr = CacheHelper.GetCacheString("AUTHEN_ROLES_STR");  //获得所有的权限管理字符串
            if (string.IsNullOrEmpty(roleStr))
            {
                roleStr = FileHelper.Instance.ReadFile(HttpContext.Current.Server.MapPath("~/static/authen.json"));

                CacheHelper.SetCache("AUTHEN_ROLES_STR", roleStr);
            }

            var roleArr = JArray.Parse(roleStr);
            if (roleArr.Any())
            {
                //取得控制器对象
                var objCon = roleArr.FirstOrDefault(o => (((JObject)o)["ControllerName"]).ToString().Trim().ToLower() == controllerName);
                if (objCon != null)
                {
                    var roleContr = objCon["Roles"].ToString();

                    if ("*".Equals(roleContr))
                    {
                        this.Roles = null;
                    }
                    else if (!string.IsNullOrEmpty(roleContr)) //controller 上面如果做了限制，就以controller为准
                    {
                        rolesStr = roleContr;
                    }
                    else  
                    {
                        JArray objActs = (JArray)objCon?.SelectToken("Actions");
                        var roleAc = objActs?.FirstOrDefault(o => (((JObject)o)["Name"]).ToString().ToLower() == actionName);
                        if (roleAc != null)
                        {
                            rolesStr = roleAc["Roles"].ToString();  //获得 action上面的角色
                        }
                    }

                }
            }

            CacheHelper.SetCache($"AUTHEN_ROLES_{controllerName}_{actionName}", string.IsNullOrEmpty(rolesStr) ? "*" : rolesStr);

            return rolesStr;
        }

        #endregion 公共方法
    }
}